/*
 * Controlador.cpp
 *
 *  Created on: 18/10/2011
 *      Author: diego
 */

#include "Controlador.h"


typedef struct header_t{
	int tamano;
}header;

Controlador::Controlador() {
	this->stpw = NULL;
	this->docs = NULL;
	this->parser = NULL;
	this->arbol = NULL;
	this->compresorTerminos = NULL;
	this->bar = NULL;
	this->memoria = NULL;

	this->serverON = false;
}

string Controlador::toString(int n) {
	stringstream Num;
	string str;

	Num << n;

	str = Num.str();

	return str;
}

void Controlador::grabarArbol(NodoABB* raiz, ofstream* out_ppal,
		ofstream* out_terminos, BitWriter* bit_wr,
		FrontCoding* compresorTerminos) {
	if (raiz == NULL) {
		return;
	}

	grabarArbol(raiz->getHijoIzq(), out_ppal, out_terminos, bit_wr,
			compresorTerminos);

	reg_indice_fijo reg_indice;
	reg_terminos_variables reg_termino;
	reg_docs_variable reg_docs;

	//cout << *(raiz->getTermino()) << endl;

	int repetidos = (int) compresorTerminos->comprimir(raiz->getTermino());

	//cout << *(raiz->getTermino()) << endl;

	reg_indice.offset_terminos = out_terminos->tellp();
	reg_indice.offset_docs = bit_wr->getPosition();
	reg_indice.distintos = raiz->getTermino()->length() + 1;
	reg_indice.repetidos = repetidos;
	reg_indice.cant_docs = raiz->getCantDocs();
	reg_indice.frec_t = raiz->getFrecuencia();

	out_ppal->write(reinterpret_cast<char *>(&reg_indice),
			sizeof(reg_indice_fijo));

	reg_termino.termino = *(raiz->getTermino());

	//out_terminos->write(reinterpret_cast<char *>(&reg_termino),sizeof(reg_terminos_variables));
	out_terminos->write(raiz->getTermino()->c_str(),
			raiz->getTermino()->length() + 1);

	IteradorLista<reg_docs_variable*> it = raiz->getIterador();
	reg_docs_variable* nodo;
	reg_docs_variable* nodo_anterior = NULL;

	while (it.tieneSiguiente()) {
		nodo = it.siguiente();
		//cout << "(" << nodo->doc_id << ", " << nodo->frec << ");";
		// guardo la lista de distancias
		if (nodo_anterior == NULL) {
			reg_docs.doc_id = nodo->doc_id;
		} else {
			reg_docs.doc_id = nodo->doc_id - nodo_anterior->doc_id;
		}

		reg_docs.frec = nodo->frec;
//		bit_wr->write(reinterpret_cast<char *>(&reg_docs),
//				sizeof(reg_docs_variable));

//Grabo los datos con codigos delta
		deltaEncode(reg_docs.doc_id, bit_wr);
		deltaEncode(reg_docs.frec, bit_wr);

		//cout << "(" <<reg_docs.doc_id << ", " << reg_docs.frec << ");";

		nodo_anterior = nodo;
	}
	//cout << endl;
	grabarArbol(raiz->getHijoDer(), out_ppal, out_terminos, bit_wr,
			compresorTerminos);

	return;
}

void Controlador::grabarArbol2(NodoABB* raiz, IndexWriter* idxWriter) {
	if (raiz == NULL) {
		return;
	}

	this->grabarArbol2(raiz->getHijoIzq(), idxWriter);

	super_reg* registro = new super_reg;
	registro->termino = *(raiz->getTermino());
	registro->frec_t = raiz->getFrecuencia();
	registro->cant_docs = raiz->getCantDocs();
	registro->lista = raiz->getLista();
/*
	registro->lista = new Lista<reg_docs_variable*>();

	IteradorLista<reg_docs_variable*> it = raiz->getIterador();
	while (it.tieneSiguiente()){
		registro->lista->agregar(it.siguiente());
	}
*/
	//cout << registro.termino << endl;
	idxWriter->write(registro);

	//this->borrar_registro(registro);
	delete registro;

	this->grabarArbol2(raiz->getHijoDer(), idxWriter);

	return;
}

void Controlador::borrar_registro(super_reg* reg){
	/*
	IteradorLista<reg_docs_variable*> it = reg->lista->iterador();
	while (it.tieneSiguiente()){
		delete it.siguiente();
	}
*/
	delete reg->lista;
	delete reg;
}

vector<string> Controlador::readDirectory(const string& path) {
	vector<string> result;
	dirent* dirEntry;
	DIR* dir;
	unsigned char isFile = 0x8;
	unsigned char isFolder = 0x4;

	//Abre el directorio
	dir = opendir(path.empty() ? "." : path.c_str());
	if (dir) {
		//Itera por el directorio
		while (true) {
			dirEntry = readdir(dir);
			if (dirEntry == NULL) {
				break;
			}
			/*Si es un archivo lo agrega al vector, si no se llama a la
			 funcion de forma recursiva*/
			if (dirEntry->d_type == isFile) {
				string filePath = path + dirEntry->d_name;
				result.push_back(filePath);
			} else if (dirEntry->d_type == isFolder) {
				string folder(dirEntry->d_name);
				if (folder != "." && folder != "..") {
					string recPath = path + dirEntry->d_name + "/";
					vector<string> files = readDirectory(recPath);
					for (size_t i = 0; i < files.size(); i++) {
						result.push_back(files[i]);
					}
				}
			}
		}
		closedir(dir);
	}
	return result;
}

void Controlador::indexar(const string& path) {
	/*### con esto mido el tiempo de ejecucion ##################*/
	clock_t inicio = clock();
	/*###########################################################*/

	Configuracion* conf = Configuracion::getConf(PATH_CONF);

	this->stpw = new Stopwords();
	this->docs = new Documentos();
	this->parser = new Parser(this->stpw);
	this->arbol = new ArbolBinario();
	this->compresorTerminos = new FrontCoding(conf->front_coding());
	this->bar = new ProgressBar(50);
	this->memoria = new MonitorMemoria(conf->max_mem_indexdor());

	vector<string> files = this->readDirectory(path);
	//sort(files.begin(), files.end());
	IndexWriter* idxWriter;
	int cont = 0;
	int porcentaje = 0;
	int cont_palabras = 0;

	this->bar->iniciar();

	for (size_t i = 0; i < files.size(); i++) {

		//this->docs->agregarDocumentos(files[i]);
		//cout << "Procesando documento " << i + 1 << " de: " << files.size() << ": " << files[i] << endl;
		this->parser->setDocument(files[i]);

		string palabra;
		while (!this->parser->eof()) {
			if (!this->memoria->hayMemoria()) {
				cont++;

				idxWriter = new IndexWriter(toString(cont), conf->front_coding());

				this->grabarArbol2(this->arbol->getRaiz(), idxWriter);
				//cout << endl << endl << endl << "### FIN ARCHIVO " << cont << endl << endl << endl << endl ;
				idxWriter->close();

				delete this->arbol;
				this->arbol = new ArbolBinario();
				this->memoria->reset();

				delete idxWriter;
			}

			palabra = this->parser->nextWord();
			cont_palabras++;

			if (!this->stpw->isStopword(palabra)) {
				this->arbol->agregarTermino(new string(palabra),
						this->docs->getUltimoId(), this->memoria);
			}
		}

		this->docs->agregarDocumentos(files[i], cont_palabras);

		porcentaje = (i * 100) / files.size();
		//cout << porcentaje << endl;
		this->bar->update(porcentaje);

		this->parser->close();
	}

	// guardo el pedazo de arbol que quedo
	cont++;
	idxWriter = new IndexWriter(toString(cont), conf->front_coding());

	this->grabarArbol2(this->arbol->getRaiz(), idxWriter);
	//cout << endl << endl << endl << "### FIN ARCHIVO " << cont << endl << endl << endl << endl ;
	idxWriter->close();

	this->bar->finalizar();
	delete this->arbol;
	this->arbol = NULL;
	this->memoria->reset();

	delete idxWriter;
	Configuracion::deleteConf();

	/*### con esto mido el tiempo de ejecucion ##################*/
	double diff = (double) (clock() - inicio) / CLOCKS_PER_SEC;
	int min = diff / 60;
	int decimales = (int) (diff * 100.0) - ((int) diff * 100);
	double segs = ((int) diff) % 60;
	cout << "Se procesaron " << files.size() << " documentos generando " << cont
			<< " sub-indices" << endl;
	cout << "en un tiempo de " << min << " minutos, " << segs << "."
			<< decimales << " segundos" << endl;
	/*###########################################################*/


	Merge* merge = new Merge();
	merge->mergeIndices_Nvias(cont);

	this->stpw->extraerStopwords();

	delete merge;

	/*### con esto mido el tiempo de ejecucion ##################*/
	double diff_final = (double) (clock() - inicio) / CLOCKS_PER_SEC;
	min = diff_final / 60;
	decimales = (int) (diff_final * 100.0) - ((int) diff_final * 100);
	segs = ((int) diff_final) % 60;
	cout << "Tiempo total utilizado para la indexacion: " << min << " minutos, " << segs << "."
			<< decimales << " segundos" << endl;
	/*###########################################################*/
/*
	cout << "durmiendo..." << endl;
	sleep(30);
*/
	// TEST PARA VER IMPRIMIR EL INDICE
/*
	IteradorIndice* it = new IteradorIndice();
	//it->setPaso(8);
	//it->setPosicion(8);
	super_reg* reg;
	reg_docs_variable* reg_lista;

	while (it->tieneSiguiente()){
		reg = it->proximoRegistro();
		cout << reg->termino << ": " << reg->frec_t << " --> ";

		IteradorLista<reg_docs_variable*> it_lista = reg->lista->iterador();
		while(it_lista.tieneSiguiente()){
			reg_lista = it_lista.siguiente();
			cout << "(" << reg_lista->doc_id << ", " << reg_lista->frec << ");";
		}

		cout << endl;

		delete reg->lista;
		delete reg;
	}

	delete it;

*/
}

void Controlador::buscar(string query) {
	/*### con esto mido el tiempo de ejecucion ##################*/
	clock_t inicio = clock();
	/*###########################################################*/

	int sock_descriptor, port_number, n;
	struct sockaddr_in server_addr;
	struct hostent *server;

	char buffer[256];
	/*
	 if (argc < 3) {
	 fprintf(stderr,"modo de uso: %s hostname port\n", argv[0]);
	 exit(0);
	 }
	 */
	//port_number = atoi(argv[2]);
	port_number = 9025;

	sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);

	if (sock_descriptor < 0) {
		cout << "fallo al abrir el socket" << endl;
		return;
	}

	// meto en el struct el host
	//server = gethostbyname(argv[1]);
	server = gethostbyname("127.0.0.1");

	if (server == NULL) {
		fprintf(stderr, "no existe el host\n");
		exit(0);
	}

	// inicializo a cero el struct
	bzero((char *) &server_addr, sizeof(server_addr));

	server_addr.sin_family = AF_INET;

	bcopy((char *) server->h_addr, (char *) &server_addr.sin_addr.s_addr,
			server->h_length);

	server_addr.sin_port = htons(port_number);

	if (connect(sock_descriptor, (struct sockaddr *) &server_addr,
			sizeof(server_addr)) < 0) {
		cout << "error al conectarse" << endl;
	}


	bzero(buffer, 256);

	// leo desde standar input el msg
	//fgets(buffer,255,stdin);
	strncpy(buffer, query.c_str(), 255);
	// escribo en el socket el msg
	n = write(sock_descriptor, buffer, strlen(buffer));

	if (n < 0) {
		cout << "fallo al escribir el socket" << endl;
	}

	if (query != "#_KILL_SERVER_#"){
		header head;

		// leo la cabecera
		n = read(sock_descriptor, &head, sizeof(head));

		if (n < 0) {
			cout << "fallo al leer desde el socket" << endl;
		}

		char* result = new char[head.tamano];
		bzero(result, head.tamano);
		//leo el resultado de la consulta
		n = read(sock_descriptor, result, head.tamano);

		if (n < 0) {
			cout << "fallo al leer desde el socket" << endl;
		}

		stringstream ss;
		string id_str = "";
		int id;
		Documentos* doc = new Documentos();
		cout << endl;
		cout << "Resultados para la consulta '" << query << "':" << endl;

		char* pch = strtok (result," ");
		int cont = 1;
		while (pch != NULL){
			id_str = pch;
			//cout << id_str << endl;
			ss << id_str;
			ss >> dec >> id;
			ss.clear();
			cout << "    " << cont << " - " << doc->getPath(id) << endl;
			pch = strtok(NULL, " ");
			cont++;
		}
		cout << endl;

		/*### con esto mido el tiempo de ejecucion ##################*/
		double diff_final = (double) ((clock() - inicio)*1.0) / (CLOCKS_PER_SEC*1.0);
		cout << "Tiempo utilizado para la cosulta: " << diff_final << " segundos" << endl;
		/*###########################################################*/
	}

	close(sock_descriptor);
	return;
}

void Controlador::flushIndex() {
	system("./cleanIndex.sh");
	return;
}

void Controlador::startServer() {
	if (!this->file_exist(".server.run")){
		system("./server & > /dev/null");
		cout << "Iniciando servidor...... [OK]" << endl;
	}else{
		cout << "El servidor ya esta corriendo" << endl;
	}
	return;
}

void Controlador::stopServer() {
	if (this->file_exist(".server.run")){
		this->buscar("#_KILL_SERVER_#");
	}

	cout << "Deteniendo Servidor..... [OK]" << endl;
}

void Controlador::restartServer() {
	if (this->file_exist(".server.run")){
		this->buscar("#_KILL_SERVER_#");
	}
	cout << "Deteniendo Servidor..... [OK]" << endl;

	system("./server & > /dev/null");
	cout << "Iniciando servidor...... [OK]" << endl;

	return;
}

bool Controlador::file_exist(string path){
	ifstream ifile(path.c_str());
	return ifile;
}

Controlador::~Controlador() {
	if (this->arbol != NULL) {
		delete this->arbol;
	}

	if (this->bar != NULL) {
		delete this->bar;
	}

	if (this->stpw != NULL) {
		delete this->stpw;
	}

	if (this->parser != NULL) {
		delete this->parser;
	}

	if (this->docs != NULL) {
		delete this->docs;
	}

	if (this->compresorTerminos != NULL) {
		delete this->compresorTerminos;
	}

	if (this->memoria != NULL) {
		delete this->memoria;
	}
}

